<template lang="html">
  <div class="org-management-wrapper">
    <div class="operating-buttons">
      <el-button type="primary" @click="uploadFile">导入</el-button>
      <el-button type="primary" @click="exportFile">导出</el-button>
      <el-button type="primary" @click="downloadTemplate">模板下载</el-button>
      <a ref="outFile"></a>
      <input type="file" @change="importFile(this)" ref="imFile"  style="display:none" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" />
    </div>
    <div class="org-chart-wrapper">
      <div class="org-chart" ref="orgChart">
      </div>
      <!-- 机构详细信息 -->
      <transition name="el-fade-in-linear">
        <el-card class="popover-panel"
          v-if="popoverPanel.show"
          :style="{top: popoverPanel.pos.top, left: popoverPanel.pos.left}"
          ref="popoverPanel">
          <div slot="header" class="clearfix header">
            <span>机构详细信息</span>
            <div class="close-btn" @click="hidePopoverPanel">
              <i class="el-icon-circle-close"></i>
            </div>
          </div>
          <div class="content">
            <dl class="definition-list clearfix">
              <dt>名称</dt>
              <dd>{{selectedOrg.name}}</dd>
              <dt>简称</dt>
              <dd>{{selectedOrg.shortName}}</dd>
              <dt>电话</dt>
              <dd>{{selectedOrg.tel}}</dd>
              <dt>地址</dt>
              <dd>{{selectedOrg.address}}</dd>
              <dt>备注</dt>
              <dd>{{selectedOrg.comments}}</dd>
            </dl>
          </div>
          <div class="button-list">
            <el-button type="primary" class="button" @click="addNode">添加</el-button>
            <el-button type="info" class="button" @click="editNode">编辑</el-button>
            <el-button type="danger" class="button" @click="delNode">删除</el-button>
          </div>
        </el-card>
      </transition>
    </div>

    <!-- 新增机构对话框 -->
    <el-dialog title="新增机构" :visible.sync="newOrgDialogVisible">
      <el-form :model="newOrg" label-width="120px">
        <el-form-item label="父机构名称">
          <span>{{selectedOrg.name}}</span>
        </el-form-item>
        <el-form-item label="名称">
          <el-input v-model="newOrg.name"></el-input>
        </el-form-item>
        <el-form-item label="简称">
          <el-input v-model="newOrg.shortName"></el-input>
        </el-form-item>
        <el-form-item label="电话">
          <el-input v-model="newOrg.tel"></el-input>
        </el-form-item>
        <el-form-item label="地址">
          <el-input v-model="newOrg.address"></el-input>
        </el-form-item>
        <el-form-item label="备注">
          <el-input v-model="newOrg.comments"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="saveAdding">保存</el-button>
      </div>
    </el-dialog>

    <!-- 机构编辑对话框 -->
    <el-dialog title="机构信息编辑" :visible.sync="dialogFormVisible">
      <el-form :model="selectedOrg" label-width="120px">
        <el-form-item label="名称">
          <el-input v-model="selectedOrg.name"></el-input>
        </el-form-item>
        <el-form-item label="简称">
          <el-input v-model="selectedOrg.shortName"></el-input>
        </el-form-item>
        <el-form-item label="电话">
          <el-input v-model="selectedOrg.tel"></el-input>
        </el-form-item>
        <el-form-item label="地址">
          <el-input v-model="selectedOrg.address"></el-input>
        </el-form-item>
        <el-form-item label="备注">
          <el-input v-model="selectedOrg.comments"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="saveEditing">保存</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
  import echarts from 'echarts'
  import { debounce, deepClone } from '@/utils'
  import { getOrgTree, importOrgs, updateOrg, deleteOrg, addOrg } from '@/api/organizations'
  var XLSX = require('xlsx')

  export default {
    data() {
      return {
        popoverPanel: {
          show: false,
          pos: {
            top: '0px',
            left: '0px'
          }
        },
        dialogFormVisible: false,
        newOrgDialogVisible: false,
        newOrg: {
          id: '',
          parentId: '',
          name: '',
          shortName: '',
          tel: '',
          address: '',
          comments: '',
        },
        formChanged: false,
        selectedOrg: {},
        chart: null,
        chartData: {},
        rABS: true
      }
    },
    methods: {
      // 绘制表格
      initChart() {
        this.chart = echarts.init(this.$refs.orgChart)
      },
      setChart(){
        this.chart.on('mouseover', params => {
          if (params.componentType === 'series') {
              // 鼠标移动到了 markPoint 上,显示信息卡片
              this.selectedOrg = params.data
              this.popoverPanel.show = true
              this.$nextTick(() => {
                // 设置信息卡片的位置
                this.setPopoverPanel(params.event.event.layerY + 10, params.event.event.layerX + 10)
              })
          }else {
            return
          }
        });
        this.chart.on('mouseout', params => {
          var t
          t = setTimeout(() => {
            this.popoverPanel.show = false
          }, 500)
          if(this.popoverPanel.show){
            this.$refs.popoverPanel.$el.addEventListener('mouseover', () => {
              console.log('mouseover')
              clearTimeout(t)
            }, false)
            // this.$refs.popoverPanel.$el.addEventListener('mouseout', (event) => {
            //   var e = event.toElement || event.relatedTarget;
            //   if (e.parentNode == this || e == this) {
            //      return;
            //   }
            //   setTimeout(() => {
            //     this.popoverPanel.show = false
            //   }, 500)
            // }, false)
          }
        })
        this.chart.setOption({
          tooltip: {
            trigger: 'none',
            triggerOn: 'mousemove'
          },
          series: [
              {
                  type: 'tree',
                  data: [this.chartData],

                  top: '1%',
                  left: '15%',
                  bottom: '1%',
                  right: '15%',

                  symbolSize: 12,

                  label: {
                      normal: {
                          position: 'left',
                          verticalAlign: 'middle',
                          align: 'right',
                          fontSize: 12
                      }
                  },

                  leaves: {
                      label: {
                          normal: {
                              position: 'right',
                              verticalAlign: 'middle',
                              align: 'left'
                          }
                      }
                  },
                  expandAndCollapse: true,
                  animationDuration: 550,
                  animationDurationUpdate: 750
              }
          ]
        })
      },
      refreshChart(){
        this.chart.dispose()
        this.chart = null
        this.initChart()
        this.setChart()
        // this.chart.setOption({
        //   series: [
        //     {
        //       data: [this.chartData]
        //     }
        //   ]
        // })
      },
      // 隐藏节点信息卡片
      hidePopoverPanel(){
        this.popoverPanel.show = false
      },
      setPopoverPanel(top, left){
        var largestHeight, largestWidth
        largestHeight = this.$refs.orgChart.offsetHeight - this.$refs.popoverPanel.$el.offsetHeight - 25;
        largestWidth = this.$refs.orgChart.offsetWidth - this.$refs.popoverPanel.$el.offsetWidth - 25;
        if (top > largestHeight) top = top - this.$refs.popoverPanel.$el.offsetHeight - 25

        if (left > largestWidth) left = largestWidth

        this.popoverPanel.pos.top = top + 'px'
        this.popoverPanel.pos.left = left + 'px'
      },

      // 给定key("id","name"等)和value，从obj中查找到第一个匹配到的对象并删除对应的
      removeNode(obj, key, value) {
        if(!obj.children || obj.children.length < 0){
          return
        }else{
          obj.children.forEach((item, index) => {
            if(item[key] === value){
              obj.children.splice(index, 1)
              return
            }else{
              this.removeNode(item, key, value)
            }
          })
        }
      },
      // 根据传入的key和value从targetObj中检索到该子节点,并用传入的source替换掉该子节点
      updateNode(targetObj, key, value, source) {
        if(targetObj[key] === value){
          for (var key in targetObj) {
            // 由于编辑信息框中没有编辑子节点信息,因此跳过复制children属性
            if(key === 'children'){
              continue
            }
            if(targetObj[key] != source[key]){
              targetObj[key] = source[key]
            }
          }
        }else{
          // 如果没有子节点return
          if(!targetObj.children || targetObj.children.length < 0){
            return
          }
          targetObj.children.forEach((item, index) => {
            this.updateNode(item, key, value, source)
          })
        }
      },
      // 向根据key(name,id等)匹配到的节点中添加子节点
      createNode(targetObj, key, value, source) {
        if(targetObj[key] === value){
          if(!targetObj.children){
            targetObj.children = []
          }
          // 将待添加的节点数据对象复制到一个临时对象中去
          var tempObj = deepClone(source)
          targetObj.children.push(tempObj)
        }else{
          if(!targetObj.children || targetObj.children.length < 0){
            return
          }
          targetObj.children.forEach((item, index) => {
            if(item[key] === value){
              if(!item.children){
                item.children = []
              }
              var tempObj = deepClone(source)
              item.children.push(tempObj)
            }else{
              this.createNode(item, key, value, source)
            }
          })
        }
      },

      // 点击删除机构按钮
      delNode(){
        this.hidePopoverPanel()
        if(this.selectedOrg.parentId === 0){
          this.$message({
            message: '无法删除根节点',
            type: 'warning'
          });
          return
        }
        this.$confirm(`此操作将永久删除节点  ${this.selectedOrg.name}, 是否继续?`, '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          deleteOrg(this.selectedOrg.id).then(res => {
            this.removeNode(this.chartData, 'id', this.selectedOrg.id)
            this.refreshChart()
            this.$message({
              type: 'success',
              message: '删除成功!'
            });
          })
        }).catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          });
        });
      },

      // 点击编辑按钮
      editNode(){
        this.hidePopoverPanel()
        this.dialogFormVisible = true
      },
      // 确定更改
      saveEditing(){
        this.dialogFormVisible = false
        updateOrg(this.selectedOrg).then(res => {
          this.updateNode(this.chartData, 'id', this.selectedOrg.id, this.selectedOrg)
          this.refreshChart();
          this.$message.success('更新成功')
        })
      },

      // 点击增加机构按钮
      addNode(){
        this.hidePopoverPanel()
        this.newOrgDialogVisible = true;
      },
      // 确认增加
      saveAdding(){
        addOrg(this.selectedOrg.id, this.newOrg).then(res => {
          this.newOrg.id = res.data.id
          this.newOrg.parentId = this.selectedOrg.id
          this.createNode(this.chartData, 'id', this.selectedOrg.id, this.newOrg)
          this.refreshChart()
          this.$message.success('新增成功')
          // 初始化newOrg数据
          this.newOrg.name = ''
          this.newOrg.shortName = ''
          this.newOrg.tel = ''
          this.newOrg.address = ''
          this.newOrg.comments = ''
          this.newOrgDialogVisible = false
        })
      },
      uploadFile(){
        this.$refs.imFile.click()
      },
      // 导入excel
      importFile() {
        this.fullscreenLoading = true
				let obj = this.$refs.imFile
				if(!obj.files) {
					this.fullscreenLoading = false
					return
				}
				var f = obj.files[0]
				var reader = new FileReader()
				reader.onload = (e) => {
					var data = e.target.result
					if(this.rABS) {
            // 手动转化
						this.wb = XLSX.read(btoa(this.fixdata(data)), {
							type: 'base64'
						})
					} else {
						this.wb = XLSX.read(data, {
							type: 'binary'
						})
					}
					let json = XLSX.utils.sheet_to_json(this.wb.Sheets[this.wb.SheetNames[0]])
					console.log(json)
          // analyzeData: 解析导入数据
					this.dealFile(this.analyzeData(json))
				}
				if(this.rABS) {
					reader.readAsArrayBuffer(f)
				} else {
					reader.readAsBinaryString(f)
				}
			},
      fixdata: function(data) { // 文件流转BinaryString
				var o = ''
				var l = 0
				var w = 10240
				for(; l < data.byteLength / w; ++l) {
					o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)))
				}
				o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)))
				return o
			},
      // 此处可以解析导入数据
      analyzeData: function(data) {
				return data
			},
      // 处理导入的数据
			dealFile: function(data) {
				this.$refs.imFile.value = ''
				importOrgs(data).then(res => {
          this.chartData = res.data
          this.refreshChart()
          this.$message({
            message: res.msg,
            type: 'success'
          });
        })
			},
      downloadFile(rs) {
				let data = [{}]
				for(let k in rs[0]) {
					data[0][k] = k
				}
				data = data.concat(rs)
				this.downloadExl(data, '机构列表')
			},
      // 导出到excel
      downloadExl(json, downName, type) {
        const uA = window.navigator.userAgent
        const isIE = /msie\s|trident\/|edge\//i.test(uA) && !!("uniqueID" in document || "documentMode" in document || ("ActiveXObject" in window) || "MSInputMethodContext" in window)
        let keyMap = [] // 获取键
        for(let k in json[0]) {
        	keyMap.push(k)
        }
        console.info('keyMap', keyMap, json)
        let tmpdata = [] // 用来保存转换好的json
        json.map((v, i) => keyMap.map((k, j) => Object.assign({}, {
        	v: v[k],
        	position: (j > 25 ? this.getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1)
        }))).reduce((prev, next) => prev.concat(next)).forEach(function(v) {
        	tmpdata[v.position] = {
        		v: v.v
        	}
        })
        let outputPos = Object.keys(tmpdata) // 设置区域,比如表格从A1到D10
        let tmpWB = {
        	SheetNames: ['mySheet'], // 保存的表标题
        	Sheets: {
        		'mySheet': Object.assign({},
        			tmpdata, // 内容
        			{
        				'!ref': outputPos[0] + ':' + outputPos[outputPos.length - 1] // 设置填充区域
        			})
        	}
        }
        let tmpDown = new Blob([this.s2ab(XLSX.write(tmpWB, {
        		bookType: (type === undefined ? 'xlsx' : type),
        		bookSST: false,
        		type: 'binary'
        	} // 这里的数据是用来定义导出的格式类型
        ))], {
        	type: ''
        }) // 创建二进制对象写入转换好的字节流
        var href = URL.createObjectURL(tmpDown) // 创建对象超链接
        this.$refs.outFile.download = downName + '.xlsx' // 下载名称
        this.$refs.outFile.href = href // 绑定a标签
        if (isIE) {
          // 兼容IE11无法触发下载的问题
          navigator.msSaveBlob(tmpDown, `${downName}.xlsx`);
        } else {
          this.$refs.outFile.click() // 模拟点击实现下载
        }
        setTimeout(function() { // 延时释放
        	URL.revokeObjectURL(tmpDown) // 用URL.revokeObjectURL()来释放这个object URL
        }, 100)
      },
      s2ab: function(s) { // 字符串转字符流
				var buf = new ArrayBuffer(s.length)
				var view = new Uint8Array(buf)
				for(var i = 0; i !== s.length; ++i) {
					view[i] = s.charCodeAt(i) & 0xFF
				}
				return buf
			},
      getCharCol: function(n) { // 将指定的自然数转换为26进制表示。映射关系：[0-25] -> [A-Z]。
				let s = ''
				let m = 0
				while(n > 0) {
					m = n % 26 + 1
					s = String.fromCharCode(m + 64) + s
					n = (n - m) / 26
				}
				return s
			},
      exportFile(){
        var source = this.chartData
        var rs = this.tree2flat(source, [])
        this.downloadFile(rs)
      },
      // 将一个树形对象(具有children属性)转化为一个表格数组,调用时需要传入target为一个空数组
      tree2flat(source, target){
        var tmpObj = {}
        for(var key in source){
          if (key === 'children') continue
          tmpObj[key] = source[key]
        }
      	target.push(tmpObj)
        if (source.children && source.children.length){
          source.children.forEach(item => {
            this.tree2flat(item, target)
          })
        }
        return target
      },
      downloadTemplate(){
        var tmpData = [
          {
            "id": "id",
            "parentId": "parentId",
            "name": "name",
            "shortName": "shortName",
            "tel": "tel",
            "address": "address",
            "order": "order",
            "comments": "comments"
          },
          {
            "id": "部门ID（父ID是0代表根节点，后续子节点在父ID基础上+三位数字）",
            "parentId": "父部门ID",
            "name": "部门名称",
            "shortName": "部门简称",
            "tel": "电话（可选）",
            "address": "地址（可选）",
            "order": "排序（0，1，2）,可选",
            "comments": "备注（可选）"
          },
          {
            "id": "1",
            "parentId": "0",
            "name": "北京数字天堂信息科技有限责任公司",
            "shortName": "数字天堂",
            "tel": "",
            "address": "",
            "order": "",
            "comments": ""
          }
        ]
        this.downloadExl(tmpData, '机构模板')
      }
    },
    created(){
    },
    mounted() {
      this.initChart()
      this.chart.showLoading()
      getOrgTree().then(res => {
        this.chartData = res.data
        this.setChart()
        this.chart.hideLoading()
      })
      this.__resizeHanlder = debounce(() => {
        if (this.chart) {
          this.chart.resize()
        }
      }, 100)
      window.addEventListener('resize', this.__resizeHanlder)
    },
    beforeDestroy() {
      if (!this.chart) {
        return
      }
      window.removeEventListener('resize', this.__resizeHanlder)
      this.chart.dispose()
      this.chart = null
    },
  }
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
  .org-management-wrapper{
    position: relative;
    width: 100%;
    .org-chart-wrapper{
      position: relative;
      // width: 100%;
      // height: 800px;
      .org-chart{
        width: 100%;
        height: calc(100vh - 130px)
      }
      .popover-panel{
          position: absolute;
          // left: 50%;
          // top: 50%;
          // transform: translate(-50%, -50%);
          .header{
            .close-btn{
              position: absolute;
              right: 10px;
              top: 10px;
              width: 20px;
              height: 20px;
              i{
                width: 100%;
                height: 100%;
                font-size: 20px;
                color: #271313;
              }
            }
          }
          .content{
            .definition-list{
              dt{
                font-weight: bold;
                clear: left;
              }
              dt, dd{
                float: left;
                display: inline-block;
                line-height: 22px;
              }
            }
          }
      }
    }

  }

</style>
